#ifndef _DISTANCE_FIELD_GRID_2_H_
#define _DISTANCE_FIELD_GRID_2_H_

#include <utility>
#include <vector>
#include <algorithm>
#include <functional>
#include <cmath>

#ifdef WIN32
#include <Windows.h>
#endif
#include <GL/GL.h>

#ifdef _CILK_
#include <cilk/cilk.h>
#endif

#include "common/containers.h"
#include "common/polytope.h"
#include "common/field_grid_2.h"


/*
** The distance field expressed in a form of 2D grid
*/
namespace Field_packing
{
	using namespace Packing_common;

	class Distance_field_grid_2
	{	
		typedef CGAL::Exact_predicates_inexact_constructions_kernel::Point_2 Point_2;
		typedef CGAL::Exact_predicates_inexact_constructions_kernel::Line_2	Line_2;
		typedef CGAL::Exact_predicates_inexact_constructions_kernel::Vector_2	Vector_2;
		typedef CGAL::Exact_predicates_inexact_constructions_kernel::Aff_transformation_2	Transformation_2;
		/*
		** The node class used in the distance field
		*/
	private:
		struct DistanceNode
		{
			std::vector<double> dist_polygon; // acctually squared distance up to now
			//  std::vector<Distance_field_grid_2::Point_2>	local_coords; // coordinates in the local frame of polygons
			std::vector<Vector_2> gradients; // the gradient of squared distance defined on this edge
			std::vector<Point_2> closest_pnts; // to which edge this node is the closest for a polygon

			double x;
			double y;

			// for reconstructing
			inline void clear() 	{	dist_polygon.clear(); /*local_coords.clear();	*/ }
		};

		struct LocalFieldNode
		{
			double dist;
			Vector_2 grad;
			Point_2 closest_pnt;
		};

	public:
		Distance_field_grid_2(const Container_box& container, size_t nb_rows, size_t nb_cols);
		~Distance_field_grid_2();

		// compute and store the distance field generated by one polygon
		void append_distance_field(const Packing_polygon& polygon) ;

		// after one polygon is transformed by "trans_param", update the correponding field
		void update_distance_field(size_t polygon_id, const Transformation_2& transformation);

		// distance query in global grid
		double distance_query(const Point_2& p, size_t polygon_id) const ;
		double gradient_query(const Point_2& p, size_t polygon_id, Vector_2& grad, Point_2& closest_point) const;

		// compute the coordinate of each grid node in the local frame of polygons
		// void append_local_coordinate(const Local_frame_2& lf);

		void clear() ;

		// draw
		void gl_draw(size_t polygon_id) const; // draw the field generated by polygon[id]
		void gl_draw_local_field(size_t polygon_id) const ; // time consuming, only for debug

	private:
		/* 
		** return the distance in a field grid using interpolation 
		** and gradient and closest point in the local coordinate
		*/
		double local_query(const Point_2& p, size_t polygon_id, Vector_2& grad, Point_2& closest_point) const; 

	private:
		Grid_2<DistanceNode> grid;
		std::vector<Local_frame_2> local_frames; // local frames with the polygons
		std::vector< Grid_2<LocalFieldNode>* > local_fields; // the distance field expressed in the local coordinate system of each polygon
		// for debug and display
		double max_dist;
	};
}
// insert an array of polygons once
//template <class InputIter>
//inline void construct_distance_field(InputIter first, InputIter last)
//{
//	grid.for_each_node(std::mem_fun_ref(&DistanceNode::clear));
//	std::for_each(local_fields.begin(), local_fields.end(), std::mem_fun(Grid_2<double>::~Grid_2<double>));
//	local_fields.clear();
//	while (first != last)	append_distance_field(*first++);
//}
#endif